Python + Astronomy

This course will be an introduction to Astropy, a maturing library for astronomy routines and tools in Python.

Astropy started as a combination of various common Python libraries (Pyfits, PyWCS, asciitables, and others) and is working towards providing a consistent API with capabilities for all astronomers. It is developed with extensive automated testing, long-term stable releases, extensive documentation, and a friendly community for contributions.

Note that this design differs from the IDL Astronomy User's Library, which is essentially a mishmash of routines.

These tutorials make some use of the examples at:


In [1]:
# First, make sure this works:
import astropy
# If this doesn't work, raise your hand!

Using FITS files in Python

FITS files are the commonly used data format in astronomy: they are essentially collections of "header data units," which can be images, tables, or some other type of data.


In [2]:
# First we load the fits submodule from astropy:
from astropy.io import fits

# Then we load a fits file (here an image from the Schmidt telescope)
hdu_list = fits.open('http://data.astropy.org/tutorials/FITS-images/HorseHead.fits')
print(hdu_list)


[<astropy.io.fits.hdu.image.PrimaryHDU object at 0x7f84472a75c0>, <astropy.io.fits.hdu.table.TableHDU object at 0x7f84472b26d8>]

The FITS file contains two header data units, a Primary HDU and an ASCII table HDU (see NASA's Primer) for the different types and limitations.

We can use the hdu_list object like a list to obtain information about each HDU:


In [3]:
print(hdu_list[0].data)
print(type(hdu_list[0].data))
print(hdu_list[0].header['FILTER'])
print(hdu_list[0].shape)


[[ 7201  6642  6642 ...,  9498  9498 10057]
 [ 6642  6363  6642 ..., 10057 10616 10616]
 [ 6922  6642  6922 ..., 10337 11175 10616]
 ..., 
 [ 5412  5132  5412 ..., 13000 12580 12021]
 [ 5796  5517  5796 ..., 12546 12546 11987]
 [ 5796  5796  6076 ..., 11987 12546 12546]]
<class 'numpy.ndarray'>
OG590
(893, 891)

In [4]:
# We can also display the full header to get a better idea of what we are looking at
hdu_list[0].header


Out[4]:
SIMPLE  =                    T /FITS: Compliance                                
BITPIX  =                   16 /FITS: I*2 Data                                  
NAXIS   =                    2 /FITS: 2-D Image Data                            
NAXIS1  =                  891 /FITS: X Dimension                               
NAXIS2  =                  893 /FITS: Y Dimension                               
EXTEND  =                    T /FITS: File can contain extensions               
DATE    = '2014-01-09        '  /FITS: Creation Date                            
ORIGIN  = 'STScI/MAST'         /GSSS: STScI Digitized Sky Survey                
SURVEY  = 'SERC-ER '           /GSSS: Sky Survey                                
REGION  = 'ER768   '           /GSSS: Region Name                               
PLATEID = 'A0JP    '           /GSSS: Plate ID                                  
SCANNUM = '01      '           /GSSS: Scan Number                               
DSCNDNUM= '00      '           /GSSS: Descendant Number                         
TELESCID=                    4 /GSSS: Telescope ID                              
BANDPASS=                   36 /GSSS: Bandpass Code                             
COPYRGHT= 'AAO/ROE '           /GSSS: Copyright Holder                          
SITELAT =              -31.277 /Observatory: Latitude                           
SITELONG=              210.934 /Observatory: Longitude                          
TELESCOP= 'UK Schmidt - Doubl' /Observatory: Telescope                          
INSTRUME= 'Photographic Plate' /Detector: Photographic Plate                    
EMULSION= 'IIIaF   '           /Detector: Emulsion                              
FILTER  = 'OG590   '           /Detector: Filter                                
PLTSCALE=                67.20 /Detector: Plate Scale arcsec per mm             
PLTSIZEX=              355.000 /Detector: Plate X Dimension mm                  
PLTSIZEY=              355.000 /Detector: Plate Y Dimension mm                  
PLATERA =        85.5994550000 /Observation: Field centre RA degrees            
PLATEDEC=       -4.94660910000 /Observation: Field centre Dec degrees           
PLTLABEL= 'OR14052 '           /Observation: Plate Label                        
DATE-OBS= '1990-12-22T13:49:00' /Observation: Date/Time                         
EXPOSURE=                 65.0 /Observation: Exposure Minutes                   
PLTGRADE= 'AD2     '           /Observation: Plate Grade                        
OBSHA   =             0.158333 /Observation: Hour Angle                         
OBSZD   =              26.3715 /Observation: Zenith Distance                    
AIRMASS =              1.11587 /Observation: Airmass                            
REFBETA =        66.3196420000 /Observation: Refraction Coeff                   
REFBETAP=     -0.0820000000000 /Observation: Refraction Coeff                   
REFK1   =        6423.52290000 /Observation: Refraction Coeff                   
REFK2   =       -102122.550000 /Observation: Refraction Coeff                   
CNPIX1  =                12237 /Scan: X Corner                                  
CNPIX2  =                19965 /Scan: Y Corner                                  
XPIXELS =                23040 /Scan: X Dimension                               
YPIXELS =                23040 /Scan: Y Dimension                               
XPIXELSZ=              15.0295 /Scan: Pixel Size microns                        
YPIXELSZ=              15.0000 /Scan: Pixel Size microns                        
PPO1    =       -3069417.00000 /Scan: Orientation Coeff                         
PPO2    =       0.000000000000 /Scan: Orientation Coeff                         
PPO3    =        177500.000000 /Scan: Orientation Coeff                         
PPO4    =       0.000000000000 /Scan: Orientation Coeff                         
PPO5    =        3069417.00000 /Scan: Orientation Coeff                         
PPO6    =        177500.000000 /Scan: Orientation Coeff                         
PLTRAH  =                    5 /Astrometry: Plate Centre H                      
PLTRAM  =                   42 /Astrometry: Plate Centre M                      
PLTRAS  =                23.86 /Astrometry: Plate Centre S                      
PLTDECSN= '-       '           /Astrometry: Plate Centre +/-                    
PLTDECD =                    4 /Astrometry: Plate Centre D                      
PLTDECM =                   56 /Astrometry: Plate Centre M                      
PLTDECS =                 47.9 /Astrometry: Plate Centre S                      
EQUINOX =               2000.0 /Astrometry: Equinox                             
AMDX1   =        67.1550859799 /Astrometry: GSC1 Coeff                          
AMDX2   =      0.0431478884485 /Astrometry: GSC1 Coeff                          
AMDX3   =       -292.435619180 /Astrometry: GSC1 Coeff                          
AMDX4   =  -2.68934864702E-005 /Astrometry: GSC1 Coeff                          
AMDX5   =   1.99133423290E-005 /Astrometry: GSC1 Coeff                          
AMDX6   =  -2.37011931379E-006 /Astrometry: GSC1 Coeff                          
AMDX7   =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX8   =   2.21426387429E-006 /Astrometry: GSC1 Coeff                          
AMDX9   =  -8.12841581455E-008 /Astrometry: GSC1 Coeff                          
AMDX10  =   2.48169090021E-006 /Astrometry: GSC1 Coeff                          
AMDX11  =   2.77618933926E-008 /Astrometry: GSC1 Coeff                          
AMDX12  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX13  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX14  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX15  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX16  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX17  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX18  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX19  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX20  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY1   =        67.1593591466 /Astrometry: GSC1 Coeff                          
AMDY2   =     -0.0471363749174 /Astrometry: GSC1 Coeff                          
AMDY3   =        316.004963520 /Astrometry: GSC1 Coeff                          
AMDY4   =   2.86798151430E-005 /Astrometry: GSC1 Coeff                          
AMDY5   =  -2.00968236347E-005 /Astrometry: GSC1 Coeff                          
AMDY6   =   2.27840393227E-005 /Astrometry: GSC1 Coeff                          
AMDY7   =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY8   =   2.23885090381E-006 /Astrometry: GSC1 Coeff                          
AMDY9   =  -2.28360163464E-008 /Astrometry: GSC1 Coeff                          
AMDY10  =   2.44828851495E-006 /Astrometry: GSC1 Coeff                          
AMDY11  =  -5.76717487998E-008 /Astrometry: GSC1 Coeff                          
AMDY12  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY13  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY14  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY15  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY16  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY17  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY18  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY19  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY20  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDREX1 =        67.1532034737 /Astrometry: GSC2 Coeff                          
AMDREX2 =      0.0434354199559 /Astrometry: GSC2 Coeff                          
AMDREX3 =       -292.435438892 /Astrometry: GSC2 Coeff                          
AMDREX4 =   4.60919247070E-006 /Astrometry: GSC2 Coeff                          
AMDREX5 =  -3.21138058537E-006 /Astrometry: GSC2 Coeff                          
AMDREX6 =   7.23651736725E-006 /Astrometry: GSC2 Coeff                          
AMDREX7 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX8 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX9 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX10=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX11=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX12=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX13=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX14=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX15=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX16=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX17=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX18=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX19=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX20=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY1 =        67.1522589487 /Astrometry: GSC2 Coeff                          
AMDREY2 =     -0.0481758265285 /Astrometry: GSC2 Coeff                          
AMDREY3 =        315.995683716 /Astrometry: GSC2 Coeff                          
AMDREY4 =  -7.47397531230E-006 /Astrometry: GSC2 Coeff                          
AMDREY5 =   9.55221105409E-007 /Astrometry: GSC2 Coeff                          
AMDREY6 =   7.60954485251E-006 /Astrometry: GSC2 Coeff                          
AMDREY7 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY8 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY9 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY10=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY11=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY12=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY13=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY14=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY15=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY16=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY17=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY18=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY19=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY20=       0.000000000000 /Astrometry: GSC2 Coeff                          
ASTRMASK= 'er.mask '           /Astrometry: GSC2 Mask                           
WCSAXES =                    2 /GetImage: Number WCS axes                       
WCSNAME = 'DSS               ' /GetImage: Local WCS approximation from full plat
RADESYS = 'ICRS              ' /GetImage: GSC-II calibration using ICRS system  
CTYPE1  = 'RA---TAN          ' /GetImage: RA-Gnomic projection                  
CRPIX1  =           446.000000 /GetImage: X reference pixel                     
CRVAL1  =            85.274970 /GetImage: RA of reference pixel                 
CUNIT1  = 'deg               ' /GetImage: degrees                               
CTYPE2  = 'DEC--TAN          ' /GetImage: Dec-Gnomic projection                 
CRPIX2  =           447.000000 /GetImage: Y reference pixel                     
CRVAL2  =            -2.458265 /GetImage: Dec of reference pixel                
CUNIT2  = 'deg               ' /Getimage: degrees                               
CD1_1   =        -0.0002802651 /GetImage: rotation matrix coefficient           
CD1_2   =         0.0000003159 /GetImage: rotation matrix coefficient           
CD2_1   =         0.0000002767 /GetImage: rotation matrix coefficient           
CD2_2   =         0.0002798187 /GetImage: rotation matrix coefficient           
OBJECT  = 'data              ' /GetImage: Requested Object Name                 
DATAMIN =                 3759 /GetImage: Minimum returned pixel value          
DATAMAX =                22918 /GetImage: Maximum returned pixel value          
OBJCTRA = '05 41 06.000      ' /GetImage: Requested Right Ascension (J2000)     
OBJCTDEC= '-02 27 30.00      ' /GetImage: Requested Declination (J2000)         
OBJCTX  =             12682.48 /GetImage: Requested X on plate (pixels)         
OBJCTY  =             20411.37 /GetImage: Requested Y on plate (pixels)         

Since this is an image, we could take a look at it with the matplotlib package:


In [5]:
# If using ipython notebook:
%matplotlib inline

# Load matplotlib
import matplotlib.pyplot as plt
# Load colormaps (the default is somewhat ugly)
from matplotlib import cm

# If *not* using ipython notebook:
# plt.ion()

plt.imshow(hdu_list[0].data, cmap=cm.gist_heat)
plt.colorbar()


Out[5]:
<matplotlib.colorbar.Colorbar at 0x7f8443e809b0>

We can manipulate the HDU's in any way that we want with the astropy.io.fits submodule:


In [6]:
hdu_list[0].data /= 2
hdu_list[0].header['FAKE'] = 'New Header'
hdu_list[0].header['FILTER'] = 'Changed'
print(hdu_list[0].data)
hdu_list[0].header


[[3600 3321 3321 ..., 4749 4749 5028]
 [3321 3181 3321 ..., 5028 5308 5308]
 [3461 3321 3461 ..., 5168 5587 5308]
 ..., 
 [2706 2566 2706 ..., 6500 6290 6010]
 [2898 2758 2898 ..., 6273 6273 5993]
 [2898 2898 3038 ..., 5993 6273 6273]]
Out[6]:
SIMPLE  =                    T /FITS: Compliance                                
BITPIX  =                   16 /FITS: I*2 Data                                  
NAXIS   =                    2 /FITS: 2-D Image Data                            
NAXIS1  =                  891 /FITS: X Dimension                               
NAXIS2  =                  893 /FITS: Y Dimension                               
EXTEND  =                    T /FITS: File can contain extensions               
DATE    = '2014-01-09        '  /FITS: Creation Date                            
ORIGIN  = 'STScI/MAST'         /GSSS: STScI Digitized Sky Survey                
SURVEY  = 'SERC-ER '           /GSSS: Sky Survey                                
REGION  = 'ER768   '           /GSSS: Region Name                               
PLATEID = 'A0JP    '           /GSSS: Plate ID                                  
SCANNUM = '01      '           /GSSS: Scan Number                               
DSCNDNUM= '00      '           /GSSS: Descendant Number                         
TELESCID=                    4 /GSSS: Telescope ID                              
BANDPASS=                   36 /GSSS: Bandpass Code                             
COPYRGHT= 'AAO/ROE '           /GSSS: Copyright Holder                          
SITELAT =              -31.277 /Observatory: Latitude                           
SITELONG=              210.934 /Observatory: Longitude                          
TELESCOP= 'UK Schmidt - Doubl' /Observatory: Telescope                          
INSTRUME= 'Photographic Plate' /Detector: Photographic Plate                    
EMULSION= 'IIIaF   '           /Detector: Emulsion                              
FILTER  = 'Changed '           / Detector: Filter                               
PLTSCALE=                67.20 /Detector: Plate Scale arcsec per mm             
PLTSIZEX=              355.000 /Detector: Plate X Dimension mm                  
PLTSIZEY=              355.000 /Detector: Plate Y Dimension mm                  
PLATERA =        85.5994550000 /Observation: Field centre RA degrees            
PLATEDEC=       -4.94660910000 /Observation: Field centre Dec degrees           
PLTLABEL= 'OR14052 '           /Observation: Plate Label                        
DATE-OBS= '1990-12-22T13:49:00' /Observation: Date/Time                         
EXPOSURE=                 65.0 /Observation: Exposure Minutes                   
PLTGRADE= 'AD2     '           /Observation: Plate Grade                        
OBSHA   =             0.158333 /Observation: Hour Angle                         
OBSZD   =              26.3715 /Observation: Zenith Distance                    
AIRMASS =              1.11587 /Observation: Airmass                            
REFBETA =        66.3196420000 /Observation: Refraction Coeff                   
REFBETAP=     -0.0820000000000 /Observation: Refraction Coeff                   
REFK1   =        6423.52290000 /Observation: Refraction Coeff                   
REFK2   =       -102122.550000 /Observation: Refraction Coeff                   
CNPIX1  =                12237 /Scan: X Corner                                  
CNPIX2  =                19965 /Scan: Y Corner                                  
XPIXELS =                23040 /Scan: X Dimension                               
YPIXELS =                23040 /Scan: Y Dimension                               
XPIXELSZ=              15.0295 /Scan: Pixel Size microns                        
YPIXELSZ=              15.0000 /Scan: Pixel Size microns                        
PPO1    =       -3069417.00000 /Scan: Orientation Coeff                         
PPO2    =       0.000000000000 /Scan: Orientation Coeff                         
PPO3    =        177500.000000 /Scan: Orientation Coeff                         
PPO4    =       0.000000000000 /Scan: Orientation Coeff                         
PPO5    =        3069417.00000 /Scan: Orientation Coeff                         
PPO6    =        177500.000000 /Scan: Orientation Coeff                         
PLTRAH  =                    5 /Astrometry: Plate Centre H                      
PLTRAM  =                   42 /Astrometry: Plate Centre M                      
PLTRAS  =                23.86 /Astrometry: Plate Centre S                      
PLTDECSN= '-       '           /Astrometry: Plate Centre +/-                    
PLTDECD =                    4 /Astrometry: Plate Centre D                      
PLTDECM =                   56 /Astrometry: Plate Centre M                      
PLTDECS =                 47.9 /Astrometry: Plate Centre S                      
EQUINOX =               2000.0 /Astrometry: Equinox                             
AMDX1   =        67.1550859799 /Astrometry: GSC1 Coeff                          
AMDX2   =      0.0431478884485 /Astrometry: GSC1 Coeff                          
AMDX3   =       -292.435619180 /Astrometry: GSC1 Coeff                          
AMDX4   =  -2.68934864702E-005 /Astrometry: GSC1 Coeff                          
AMDX5   =   1.99133423290E-005 /Astrometry: GSC1 Coeff                          
AMDX6   =  -2.37011931379E-006 /Astrometry: GSC1 Coeff                          
AMDX7   =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX8   =   2.21426387429E-006 /Astrometry: GSC1 Coeff                          
AMDX9   =  -8.12841581455E-008 /Astrometry: GSC1 Coeff                          
AMDX10  =   2.48169090021E-006 /Astrometry: GSC1 Coeff                          
AMDX11  =   2.77618933926E-008 /Astrometry: GSC1 Coeff                          
AMDX12  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX13  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX14  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX15  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX16  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX17  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX18  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX19  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDX20  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY1   =        67.1593591466 /Astrometry: GSC1 Coeff                          
AMDY2   =     -0.0471363749174 /Astrometry: GSC1 Coeff                          
AMDY3   =        316.004963520 /Astrometry: GSC1 Coeff                          
AMDY4   =   2.86798151430E-005 /Astrometry: GSC1 Coeff                          
AMDY5   =  -2.00968236347E-005 /Astrometry: GSC1 Coeff                          
AMDY6   =   2.27840393227E-005 /Astrometry: GSC1 Coeff                          
AMDY7   =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY8   =   2.23885090381E-006 /Astrometry: GSC1 Coeff                          
AMDY9   =  -2.28360163464E-008 /Astrometry: GSC1 Coeff                          
AMDY10  =   2.44828851495E-006 /Astrometry: GSC1 Coeff                          
AMDY11  =  -5.76717487998E-008 /Astrometry: GSC1 Coeff                          
AMDY12  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY13  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY14  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY15  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY16  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY17  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY18  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY19  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDY20  =       0.000000000000 /Astrometry: GSC1 Coeff                          
AMDREX1 =        67.1532034737 /Astrometry: GSC2 Coeff                          
AMDREX2 =      0.0434354199559 /Astrometry: GSC2 Coeff                          
AMDREX3 =       -292.435438892 /Astrometry: GSC2 Coeff                          
AMDREX4 =   4.60919247070E-006 /Astrometry: GSC2 Coeff                          
AMDREX5 =  -3.21138058537E-006 /Astrometry: GSC2 Coeff                          
AMDREX6 =   7.23651736725E-006 /Astrometry: GSC2 Coeff                          
AMDREX7 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX8 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX9 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX10=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX11=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX12=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX13=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX14=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX15=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX16=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX17=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX18=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX19=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREX20=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY1 =        67.1522589487 /Astrometry: GSC2 Coeff                          
AMDREY2 =     -0.0481758265285 /Astrometry: GSC2 Coeff                          
AMDREY3 =        315.995683716 /Astrometry: GSC2 Coeff                          
AMDREY4 =  -7.47397531230E-006 /Astrometry: GSC2 Coeff                          
AMDREY5 =   9.55221105409E-007 /Astrometry: GSC2 Coeff                          
AMDREY6 =   7.60954485251E-006 /Astrometry: GSC2 Coeff                          
AMDREY7 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY8 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY9 =       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY10=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY11=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY12=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY13=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY14=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY15=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY16=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY17=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY18=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY19=       0.000000000000 /Astrometry: GSC2 Coeff                          
AMDREY20=       0.000000000000 /Astrometry: GSC2 Coeff                          
ASTRMASK= 'er.mask '           /Astrometry: GSC2 Mask                           
WCSAXES =                    2 /GetImage: Number WCS axes                       
WCSNAME = 'DSS               ' /GetImage: Local WCS approximation from full plat
RADESYS = 'ICRS              ' /GetImage: GSC-II calibration using ICRS system  
CTYPE1  = 'RA---TAN          ' /GetImage: RA-Gnomic projection                  
CRPIX1  =           446.000000 /GetImage: X reference pixel                     
CRVAL1  =            85.274970 /GetImage: RA of reference pixel                 
CUNIT1  = 'deg               ' /GetImage: degrees                               
CTYPE2  = 'DEC--TAN          ' /GetImage: Dec-Gnomic projection                 
CRPIX2  =           447.000000 /GetImage: Y reference pixel                     
CRVAL2  =            -2.458265 /GetImage: Dec of reference pixel                
CUNIT2  = 'deg               ' /Getimage: degrees                               
CD1_1   =        -0.0002802651 /GetImage: rotation matrix coefficient           
CD1_2   =         0.0000003159 /GetImage: rotation matrix coefficient           
CD2_1   =         0.0000002767 /GetImage: rotation matrix coefficient           
CD2_2   =         0.0002798187 /GetImage: rotation matrix coefficient           
OBJECT  = 'data              ' /GetImage: Requested Object Name                 
DATAMIN =                 3759 /GetImage: Minimum returned pixel value          
DATAMAX =                22918 /GetImage: Maximum returned pixel value          
OBJCTRA = '05 41 06.000      ' /GetImage: Requested Right Ascension (J2000)     
OBJCTDEC= '-02 27 30.00      ' /GetImage: Requested Declination (J2000)         
OBJCTX  =             12682.48 /GetImage: Requested X on plate (pixels)         
OBJCTY  =             20411.37 /GetImage: Requested Y on plate (pixels)         
FAKE    = 'New Header'                                                          

Let's write our new FITS file to our local computer. Running pwd will tell us what directory it is saved to.


In [7]:
%pwd


Out[7]:
'/home/joe/toledo/workshop/astro-python'

In [8]:
hdu_list.writeto('new-horsehead.fits', clobber=True)

Astropy ASCII file reader

While a number of ASCII file readers exist (including numpy.genfromtxt, numpy.loadtxt, and pandas.read_*), Astropy includes readers text file formats commonly used in Astronomy.

These are read as an Astropy Table object, which are convertable to numpy arrays or pandas DataFrames. These can contain unit information and there is work on-going to incoporate uncertainities.


In [9]:
# First we load the ascii submodule:
from astropy.io import ascii

example_csv = ascii.read('http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv')
print(example_csv)


Downloading http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv [Done]
             street                   city       zip  ...  latitude  longitude 
------------------------------- --------------- ----- ... --------- -----------
                   3526 HIGH ST      SACRAMENTO 95838 ... 38.631913 -121.434879
                    51 OMAHA CT      SACRAMENTO 95823 ... 38.478902 -121.431028
                 2796 BRANCH ST      SACRAMENTO 95815 ... 38.618305 -121.443839
               2805 JANETTE WAY      SACRAMENTO 95815 ... 38.616835 -121.439146
                6001 MCMAHON DR      SACRAMENTO 95824 ...  38.51947 -121.435768
             5828 PEPPERMILL CT      SACRAMENTO 95841 ... 38.662595 -121.327813
            6048 OGDEN NASH WAY      SACRAMENTO 95842 ... 38.681659 -121.351705
                  2561 19TH AVE      SACRAMENTO 95820 ... 38.535092 -121.481367
11150 TRINITY RIVER DR Unit 114  RANCHO CORDOVA 95670 ... 38.621188 -121.270555
                   7325 10TH ST       RIO LINDA 95673 ... 38.700909 -121.442979
                            ...             ...   ... ...       ...         ...
               7540 HICKORY AVE      ORANGEVALE 95662 ... 38.703056 -121.235221
            5024 CHAMBERLIN CIR       ELK GROVE 95757 ... 38.389756 -121.446246
              2400 INVERNESS DR         LINCOLN 95648 ... 38.897814 -121.324691
                5 BISHOPGATE CT      SACRAMENTO 95823 ... 38.467936 -121.445477
               5601 REXLEIGH DR      SACRAMENTO 95823 ... 38.445342 -121.441504
               1909 YARNELL WAY       ELK GROVE 95758 ... 38.417382 -121.484325
             9169 GARLINGTON CT      SACRAMENTO 95829 ... 38.457679  -121.35962
                6932 RUSKUT WAY      SACRAMENTO 95823 ... 38.499893  -121.45889
              7933 DAFFODIL WAY  CITRUS HEIGHTS 95610 ... 38.708824 -121.256803
               8304 RED FOX WAY       ELK GROVE 95758 ...    38.417 -121.397424
            3882 YELLOWSTONE LN EL DORADO HILLS 95762 ... 38.655245 -121.075915
Length = 985 rows

In [10]:
# We can also read Astronomy-specific formats.
# For example, IPAC formatted files
example_ipac = ascii.read('http://exoplanetarchive.ipac.caltech.edu/docs/tblexamples/IPAC_ASCII_one_header.tbl')
print(example_ipac)


Downloading http://exoplanetarchive.ipac.caltech.edu/docs/tblexamples/IPAC_ASCII_one_header.tbl [Done]
 object       ra          dec     
------- ------------- ------------
    M56   289.1479411   30.1845005
 ic4710  277.15820833  66.98227778
   hoix   49.38333333  69.04583333
  tol89  210.33983333 -33.06377778
    ngc 4552188.91586  12.55634139
    M82   148.9696875  69.67938333
  mrk33  158.13283333  54.40102778
ngc1097     41.579375 -30.27488889
    arp 22179.8745833 -19.29722222
   M 65  169.73316667  13.09222222

Tables support many of the same indexing and slicing operations as numpy arrays, as well as some of the higher-level operations of pandas. See the Astropy tutorial for more examples.

Units and Quantities

A nice addition to Astropy is the ability to manipulate units used in astronomy. By convention, we import this functionality into the name u:


In [11]:
from astropy import units as u

In [12]:
# SI, cgs, and other units are defined in Astropy:
u.m, u.angstrom, u.erg, u.Jy, u.solMass


Out[12]:
(Unit("m"), Unit("Angstrom"), Unit("erg"), Unit("Jy"), Unit("solMass"))

In [13]:
# Units all have documentation and attributes
print(u.solMass.names)
print(u.solMass.physical_type)


['solMass', 'M_sun', 'Msun']
mass

We can create composite units, such as units of acceleration:


In [14]:
u.m / u.second / u.second


Out[14]:
$\mathrm{\frac{m}{s^{2}}}$

In [15]:
u.pc / u.attosecond / u.fortnight


Out[15]:
$\mathrm{\frac{pc}{attosecond\,fortnight}}$

In addition to unit manipulation, Astropy has a concept of Quantities - numbers (or arrays) with units:


In [16]:
print(5*u.erg/u.second)
5*u.erg/u.second


5.0 erg / s
Out[16]:
$5 \; \mathrm{\frac{erg}{s}}$

In [17]:
import numpy as np
my_data = np.array([1,2,3,4,5,6]) * u.Hertz
print(my_data)


[ 1.  2.  3.  4.  5.  6.] Hz

In [18]:
# Quantities (and their units) can be combined through algebraic manipulation:
new_data = (6.626e-34 * u.m**2 * u.kg / u.second) * my_data
print(new_data)


[  6.62600000e-34   1.32520000e-33   1.98780000e-33   2.65040000e-33
   3.31300000e-33   3.97560000e-33] Hz kg m2 / s

Since the computer knows the physical types of each unit, it is able to make conversions between them. Let's use this to simplify my_data. The decompose method will try to use the most basic units, while the .si and .cgs will attempt simple representations with those two bases:


In [19]:
print(new_data.cgs)
print(new_data.si)
print(new_data.decompose())


[  6.62600000e-27   1.32520000e-26   1.98780000e-26   2.65040000e-26
   3.31300000e-26   3.97560000e-26] erg
[  6.62600000e-34   1.32520000e-33   1.98780000e-33   2.65040000e-33
   3.31300000e-33   3.97560000e-33] m N
[  6.62600000e-34   1.32520000e-33   1.98780000e-33   2.65040000e-33
   3.31300000e-33   3.97560000e-33] kg m2 / s2

In [20]:
# We can use the to() method to convert to anything with the same physical_type
print(new_data.unit.physical_type)
print(new_data.to(u.joule))
print(new_data.to(u.eV))


energy
[  6.62600000e-34   1.32520000e-33   1.98780000e-33   2.65040000e-33
   3.31300000e-33   3.97560000e-33] J
[  4.13562409e-15   8.27124818e-15   1.24068723e-14   1.65424964e-14
   2.06781205e-14   2.48137445e-14] eV

In [21]:
# With the to() method, unit changes are relatively straightforward:
(420*u.parsec).to(u.AU)


Out[21]:
$86631219 \; \mathrm{AU}$

Astropy also includes constants in another submodule, astropy.constants. For example, the average magnitude of the gravitational force of the Earth on the Sun, in SI units, is:


In [22]:
from astropy.constants import M_earth, G, M_sun
(G * M_earth * M_sun / u.AU**2).to(u.N)


Out[22]:
$3.5437358 \times 10^{22} \; \mathrm{N}$

Astropy will even convert units that are not physically compatible, if you are explicit about how to do the conversion. For example, the relationship between wavelength and frequency of light is defined by the choice of the speed of light, allowing the conversion of one to the other:


In [23]:
(450. * u.nm).to(u.GHz, equivalencies=u.spectral())


Out[23]:
$666205.46 \; \mathrm{GHz}$

A very useful trick is that Astropy will even convert units that require extra information to do so. For example, flux density is usually defined as a density with respect to wavelength or frequency, with the two forms convertable via: $$ \nu f_\nu = \lambda f_\lambda$$ To convert between the different definitions of flux density, we merely need to supply the wavelength or frequency used:


In [24]:
f_lambda = (1e-18 * u.erg / u.cm**2 / u.s / u.angstrom)

print(f_lambda.to(u.Jy, equivalencies=u.equivalencies.spectral_density(1*u.micron)))
print(f_lambda.to(u.Jy, equivalencies=u.equivalencies.spectral_density(299.79*u.THz)))


3.3356409519815205e-06 Jy
3.335695650530986e-06 Jy

Celestial Coordinate Systems

What about units that coorrespond to locations?

While there does exists u.degree and u.arcsecond, the essential coordinate manipulation is part of the astropy.coordinates submodule. Coordinate conversions, catalog conversions, and more are supported.


In [25]:
# Let's import the main class used, SkyCoord, and create a couple SkyCoord objects:
from astropy.coordinates import SkyCoord

print(SkyCoord(-2*u.deg, 56*u.deg))
print(SkyCoord(1*u.hourangle, 5*u.degree))
print(SkyCoord('2h2m1s 9d9m9s'))
print(SkyCoord('-2.32d', '52.3d', frame='fk4'))
print(SkyCoord.from_name("M101"))

sc = SkyCoord('25d 35d')


<SkyCoord (ICRS): (ra, dec) in deg
    (358.0, 56.0)>
<SkyCoord (ICRS): (ra, dec) in deg
    (15.0, 5.0)>
<SkyCoord (ICRS): (ra, dec) in deg
    (30.50416667, 9.1525)>
<SkyCoord (FK4: equinox=B1950.000, obstime=B1950.000): (ra, dec) in deg
    (357.68, 52.3)>
<SkyCoord (ICRS): (ra, dec) in deg
    (210.8024292, 54.34875)>

In [26]:
# We can retrive the coordinates we used to create these objects:
print(sc.ra)
print(sc.dec)


25d00m00s
35d00m00s

In [27]:
# We can transform coordinates to different representations (i.e., coordinate systems)
print(sc.transform_to('fk4'))
print(sc.transform_to('galactic'))


<SkyCoord (FK4: equinox=B1950.000, obstime=B1950.000): (ra, dec) in deg
    (24.2784772, 34.74698881)>
<SkyCoord (Galactic): (l, b) in deg
    (134.06227255, -26.81998852)>

In [28]:
# Seperations and position angles are calculatable from SkyCoord objects:
sc2 = SkyCoord('35d 25d', frame='galactic')
print(sc.separation(sc2))


108d32m47.6553s

In [29]:
# When we have a world coordinate system (e.g., from a FITS file header), we can convert to and from pixel coordinates:
from astropy.wcs import WCS
w = WCS(hdu_list[0].header)

print(sc.to_pixel(w))
print(SkyCoord.from_pixel(5, 5, w))


(array(382308.9779641907), array(317244.5334360448))
<SkyCoord (ICRS): (ra, dec) in deg
    (85.39827201, -2.58178063)>

Honorable Mentions in Astropy

These are some things that I'm not very familiar with, but I want to point out with a few quick examples.

votable

VOTables are an alternative format to FITS in use by virtual observatory projects. This one is difficult to prepare a head of time, since these are typically generated on the fly in response to search/database queries. astropy.io.votable handles these files.

Modeling

The astropy.modeling submodule is concerned with the fitting of models to data. The goal is to make it easy to fit or represent your data using common models, such as broken power laws or other composite models.

For example, here is some synthetic data that is roughly Gaussian-like:


In [30]:
import numpy as np
from astropy.modeling import models, fitting


np.random.seed(0)
x = np.linspace(-5., 5., 200)
y = 3 * np.exp(-0.5 * (x - 1.3)**2 / 0.8**2)
y += np.random.normal(0., 0.2, x.shape)
plt.plot(x, y, 'ko')
plt.xlabel('Position')
plt.ylabel('Flux')


Out[30]:
<matplotlib.text.Text at 0x7f843bbff748>

In [31]:
# Fit the data using a Gaussian
model_object = models.Gaussian1D(amplitude=1., mean=0, stddev=1.)
fiter = fitting.LevMarLSQFitter()
g = fiter(model_object, x, y)

plt.plot(x, y, 'ko')
plt.plot(x, g(x), 'r-', lw=2)
plt.xlabel('Position')
plt.ylabel('Flux')


Out[31]:
<matplotlib.text.Text at 0x7f8450aabf98>

In [32]:
# Get information about the fitted model:
print(g)


Model: Gaussian1D
Inputs: ('x',)
Outputs: ('y',)
Model set size: 1
Parameters:
      amplitude        mean         stddev    
    ------------- ------------- --------------
    3.04705246874 1.27430145809 0.813535026407

Cosmology

There is also some work for cosmology computations, specifically with different cosmologies.

For this, it is essential to load a Cosmology object. These are, by convention, named cosmo:


In [33]:
# Load the 9-year WMAP Cosmology and get H_0
from astropy.cosmology import WMAP9 as cosmo
print(cosmo.H(0))


69.32 km / (Mpc s)

In [34]:
# find the age of the universe at a given redshift:
print(cosmo.age(1))


5.922228776289075 Gyr

In [35]:
# Other cosmologies are avaliable
from astropy.cosmology import Planck13 as cosmo
print(cosmo.age(1))


5.8631661413140685 Gyr

In [36]:
# Build your own cosmology
from astropy.cosmology import FlatLambdaCDM
cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Ob0=0.05)
print(cosmo.age(1))


5.74698053003206 Gyr

Affliated Packages

I don't believe I'll have time to address these, but it is worth pointing out these useful packages:

  • APLpy: A package for using WCS coordinates in matplotlib.
  • specutils, photutils, and ccdproc: Packages for analyzing 1-D spectrum, photometry, and CCD image reduction. These are likely to be included in Astropy in the future.
  • astroquery: An interface to many public (and restricted) online resources for downloading astronomical data.